/**
 * 线程安全队列：使用锁和条件变量
 */
#include <exception>
#include <iostream>
#include <thread>
#include <mutex>
#include <queue>
#include <condition_variable>

template<typename T>
class threadSafe_queue {
public:
    threadSafe_queue() { }
    // void push(T new_value) {
    //     std::lock_guard<std::mutex> lk(mtx);
    //     data_queue.push(std::move(data));
    //     data_cond.notify_one();
    // }
    void push(T new_value) {
        std::shared_ptr<T> data(std::make_shared<T>(std::move(new_value)));
        std::lock_guard<std::mutex> lk(mtx);
        data_queue.push(std::move(data));
        data_cond.notify_one();
    }

    void wait_and_pop(T& value) {
        std::unique_lock<std::mutex> lk(mtx);
        data_cond.wait(lk, [this] {
            return !data_queue.empty();
        });
        // value = std::move(data_queue.front());
        value = std::move(*data_queue.front());
        data_queue.pop();
    }

    std::shared_ptr<T> wait_and_pop() {
        std::unique_lock<std::mutex> lk(mtx);
        data_cond.wait(lk, [this] {
            return !data_queue.empty();
        });
        // std::shared_ptr<T> res(std::make_shared<T>(std::move(data_queue.front())));
        std::shared_ptr<T> res = data_queue.front();
        data_queue.pop();
        return res;
    }

    bool try_pop(T& value) {
        std::lock_guard<std::mutex> lk(mtx);
        if (data_queue.empty()) {
            return false;
        }
        // value = std::move(data_queue.front());
        value = std::move(*data_queue.front());
        data_queue.pop();
        return true;
    }

    std::shared_ptr<T> try_pop() {
        std::lock_guard<std::mutex> lk(mtx);
        if (data_queue.empty()) {
            return std::shared_ptr<T>();
        }
        // std::shared_ptr<T> res(std::make_shared<T>(std::move(data_queue.front())));
        std::shared_ptr<T> res = data_queue.front();
        data_queue.pop();
        return res;
    }

    bool empty() const {
        std::lock_guard<std::mutex> lk(mtx);
        return data_queue.empty();
    }

private:
    mutable std::mutex mtx;
    std::queue<T> data_queue;
    std::condition_variable data_cond;
};